package com.spring;

import com.spring.annotation.Component;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author chengjy
 * @date 2025/4/7
 */
public class ApplicationContext {

    private Map<String, Object> ioc = new HashMap<>();
    private Map<String, Object> loadingIoc = new HashMap<>();
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    public ApplicationContext(String packageName) throws IOException {
        initContext(packageName);

    }

    private void initContext(String packageName) throws IOException {
        scanPage(packageName).stream().filter(this::scanCreate).
                forEach(this::wapperBeanDefinition);
        // 初始化BeanPostProcessor处理器
        initPostProcessor();
        // 创建bean
        beanDefinitionMap.values().forEach(this::CreateBean);


    }

    private void initPostProcessor() {
        beanDefinitionMap.values().stream()
                .filter(beanDefinition ->  BeanPostProcessor.class.isAssignableFrom( beanDefinition.getBeanClass()))
                .map(beanDefinition -> (BeanPostProcessor) CreateBean(beanDefinition))
                .forEach(beanPostProcessors::add);

    }

    private Object CreateBean(BeanDefinition beanDefinition) {
        String beanName = beanDefinition.getBeanName();

        if (ioc.containsKey(beanName)) {
            return ioc.get(beanName);
        }
        if (loadingIoc.containsKey(beanName)) {
            return loadingIoc.get(beanName);
        }
        return doCreateBean(beanDefinition);
    }

    private Object doCreateBean(BeanDefinition beanDefinition) {
        String beanName = beanDefinition.getBeanName();
        try {
            Constructor<?> constructor = beanDefinition.getConstructor();
            // 判断是否是有参构造
            Object object =null;
            int length = constructor.getParameterTypes().length;
            if(length >= 1){
                Object[] params = new Object[length];
                for (int i = 0; i < length; i++) {
                    Class<?> parameterType = constructor.getParameterTypes()[i];
                    params[i] = getBean( parameterType);
                }
                object = constructor.newInstance( params);
            }else{
                object = constructor.newInstance();
            }
            loadingIoc.put(beanName, object);

            autoWiredBean(beanDefinition, object);

            loadingIoc.remove(beanName);

            object = initializeBean(beanDefinition, object);

            ioc.put(beanName, object);
            return object;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    private Object initializeBean(BeanDefinition beanDefinition, Object object) throws IllegalAccessException, InvocationTargetException {

        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            object = beanPostProcessor.beforeInitializeBean(object, beanDefinition.getBeanName());
        }

        // 执行初始化方法
        if (beanDefinition.getPostConstructor() != null) {
            beanDefinition.getPostConstructor().invoke(object);
        }

        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            object = beanPostProcessor.afterInitializeBean(object, beanDefinition.getBeanName());
        }
        return object;
    }

    private void autoWiredBean(BeanDefinition beanDefinition, Object object) throws IllegalAccessException {
        for (Map.Entry<Field, String> fieldStringEntry : beanDefinition.getAutoWiredFields().entrySet()) {
            Field field = fieldStringEntry.getKey();
            String beanName = fieldStringEntry.getValue();

            field.setAccessible(true);
            Class classType = field.getType();// 类型获取bean
            field.set(object, getBean(classType));
        }
    }

    private BeanDefinition wapperBeanDefinition(Class<?> aClass) {
        BeanDefinition beanDefinition = new BeanDefinition(aClass);
        if (beanDefinitionMap.containsKey(beanDefinition.getBeanName())) {
            throw new RuntimeException("beanName 重复");
        }
        beanDefinitionMap.put(beanDefinition.getBeanName(), beanDefinition);
        return beanDefinition;
    }

    protected boolean scanCreate(Class<?> aClass) {
        if (aClass.isAnnotationPresent(Component.class)) {
            return true;
        }
        return false;
    }

    private List<Class<?>> scanPage(String packageName) throws IOException {
        List<Class<?>> classList = new ArrayList<>();
        URL resource = this.getClass().getClassLoader().getResource(packageName.replace(".", "/"));
        System.out.println(resource);
        Path path = new File(resource.getFile()).toPath();
        Files.walkFileTree(path, new SimpleFileVisitor<>(){
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {

                if (file.toString().endsWith(".class")) {
                    int indexOf = file.toString().replace("\\", ".").indexOf(packageName);
                    String classPath = file.toString().replace("\\", ".").substring(indexOf , (file.toString().length() - ".class".length()));

                    try {
                        Class<?> aClass = Class.forName(classPath);
                        classList.add(aClass);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                    }
                return FileVisitResult.CONTINUE;
            }
        });
        return classList;
    }

    public Object getBean(String beanName) {
        Object object = ioc.get(beanName);
        if (object != null) {
            return object;
        }
        object = loadingIoc.get(beanName);
        if(object != null) {
            return object;
        }

        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new RuntimeException(beanName + " 不存在");
        }
        return doCreateBean(beanDefinitionMap.get(beanName));
    }
    public <T>T getBean(Class<T> clazz) {
        String beanName = beanDefinitionMap.values().stream()
                .filter(beanDefinition -> clazz.isAssignableFrom(beanDefinition.getBeanClass()))
                .map(BeanDefinition::getBeanName)
                .findFirst().orElse(null);
        return (T) getBean(beanName);
    }
    public <T>List<T> getBeans(Class<T> clazz) {
        return beanDefinitionMap.values().stream()
                .filter(beanDefinition -> clazz.isAssignableFrom(beanDefinition.getBeanClass()))
                .map(beanDefinition -> {
                    String beanName = beanDefinition.getBeanName();
                    return (T) getBean(beanName);
                }).toList();
    }
}
